#include "treegen.h"
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include "shadow2d.h"




using namespace std;


TreeGen::TreeGen(){
    treeRoot = new Bud();
    _tropism = float3(0,TROPISM,0);
    _wind = float3(0,0,0);
    srand(468367346);
    //modifies portion sent to terminal vs aux.
     LAMBDA = .58;
    //alpha modifies resource at base
     ALPHA = 2;
    //determines rate of diameter growth (smaller the fatter)
     DIAMETER_RATE = 2.8;
    // >0 for growth influenced up, <0 for growth skewed down
     TROPISM = .3;
    //default angle of auxilary branch
     AUX_ANGLE = .75;
    //weight of default aux direction in branch growth
     AUX_WEIGHT = 3;
    //weight of default term direction in branch growth
     TERM_WEIGHT = 3;
    //scale adujst for term and aux directions
     TLENGTH = 7;
     ALENGTH = 7;
     SHED_THRES = .2;
}

TreeGen::~TreeGen(){
    freeTree(treeRoot);
}

void TreeGen::freeTree(Bud *root){
    if( root->term)
        freeTree(root->term);

    if( root->aux)
        freeTree(root->aux);

    delete root;
}

//This method gathers resources and collects at the base
void TreeGen::basipetalBH(Bud *b){
    float tq =0;

    //if no aux bud add the resource form auxilary
    if(!b->aux) tq += b->aQ;
    else{
        basipetalBH(b->aux);
        tq += b->aux->iQ;
    }
    if(!b->term) tq += b->tQ;
    else{
        basipetalBH(b->term);
        tq += b->term->iQ;
    }
    b->iQ = tq;
}

//This method should be called to collect resources from the buds
//and redistribute back from base
void TreeGen::passBH(){
    basipetalBH(treeRoot);
    this->treeRoot->resource = ALPHA * this->treeRoot->iQ;
    pass2BH(this->treeRoot);
}

//This method redistributes resources back to buds
void TreeGen::pass2BH(Bud *b){
    float qm = b->term ? b->term->iQ : b->tQ;
    float ql = b->aux ? b->aux->iQ : b->aQ;
    float qt =  (LAMBDA * qm + (1.0-LAMBDA)* ql);

    float vm = b->resource * LAMBDA * qm / qt;
    float vl =b->resource * (1.0- LAMBDA)* ql / qt;

    if(qt ==0){
        vm = 0;
        vl = 0;
    }

    if(b->aux){
        //This is not part of the algorithm but rather a quick fix to stop it from going downward
        //I think the downward tren is just an error in the algorithm.
        if(b->aux->direction.y <-.8) vl*=.5;
        b->aux->resource = vl;
        pass2BH(b->aux);
    }
    else{
        b->ares = vl;

    }

    if(b->term){
        //This is not part of the algorithm but rather a quick fix to stop it from going downward
        //I think the downward tren is just an error in the algorithm.
        if(b->term->direction.y <-.8) vm*=.5;
        b->term->resource = vm;
        pass2BH(b->term);
    }
    else{
        b->tres = vm;

    }

}

void TreeGen::createBudFromBud(Bud *b, Bud *out){
    out->budsAbove = 0;
    out->season = b->season+1;//may be wrong
    out->start = b->end;
}

void TreeGen::produceShoots(Bud *b){
    if(! b->aux){
        if( b->ares > 1.0){
            b->aux = new Bud();
            createBudFromBud(b,b->aux);
            float3 dir = AUX_WEIGHT*b->auxDir + b->aV + _tropism + (_wind*b->end.y);
            dir.normalize();
            b->aux->direction = dir;
            b->aux->length = ALENGTH* b->ares / ((float) (int) b->ares);
            b->aux->end = b->aux->length*(b->aux->direction) + b->aux->start;
            b->aux->theta = atan2(b->aux->end.y - b->aux->start.y,b->aux->end.x - b->aux->start.x);
            b->aux->diameter = 8;
            initializeBud(b->aux);

        }
    }
    else
        produceShoots(b->aux);


    if(!b->term){
        if(b->tres > 1.0){
            b->term = new Bud();
            createBudFromBud(b,b->term);
            float3 dir = TERM_WEIGHT*b->direction + b->tV + _tropism + (_wind*b->end.y);
            dir.normalize();
            b->term->direction = dir;
            b->term->length = TLENGTH * b->tres / ((float) (int) b->tres);
            b->term->end = b->term->length*(b->term->direction) + b->term->start ;
            b->term->theta = atan2(b->term->end.y - b->term->start.y,b->term->end.x - b->term->start.x);
            b->term->diameter = 8;
            initializeBud(b->term);
        }
    }
    else
        produceShoots(b->term);
}

void TreeGen::shedBranches(Bud *b){
    int num = 0;
    if(b->aux){
        shedBranches(b->aux);
        num += b->aux->budsAbove;
        num++;
    }

    if(b->term){
        shedBranches(b->term);
        num+= b->term->budsAbove;
        num++;
    }

    b->budsAbove = num;
    if(num != 0){
        //cout<<(b->resource / (double) num)<<endl;
        if((b->resource / (double) num) < SHED_THRES){
            if(b->aux)
                freeTree(b->aux);
            if(b->term)
                freeTree(b->term);
            b->aux = NULL;
            b->term = NULL;
        }
    }
}















/**

TreeGen3D::TreeGen3D(float3 start){//TODO:takes in certain parameters, specifying bud growth
treeRoot = new Bud();
treeRoot->start = start;
}
void TreeGen3D::genTree(){
    treeRoot->start = float3(0,0,0);
    treeRoot->end = float3(2,2,2);
    //TODO: hard code a tree, get this working on linux machines
 }

void TreeGen3D::updateTree(){}
float3 TreeGen3D::findDirection(){}
void TreeGen3D:: applyTropism(){}
void TreeGen3D::calculateShadow(){}
void TreeGen3D::applyShedding(){}
void TreeGen3D::chooseBudFate(){}
void TreeGen3D::createBudFromBud(Bud *b, Bud *out){
    out->budsAbove = 0;
    out->season = b->season+1;//may be wrong
    out->start = b->end;
}
void TreeGen3D::freeTree(Bud *root){
    if(! root->term)
        freeTree(root->term);

    if(! root->aux)
        freeTree(root->aux);

    delete root;

}

void TreeGen3D::calculateBranchDiameter(){}
*/

